Java 工程师成神之路!

您所在的位置:网站首页 kafka copyonwrite Java 工程师成神之路!

Java 工程师成神之路!

#Java 工程师成神之路!| 来源: 网络整理| 查看: 265

1. 面向对象 什么是面向对象 面向对象与面向过程

始终围绕如何解决具体问题进行。面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了; 以更形象的方式在计算机中构建现实的事物,更符合人认识世界的习惯。面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

面向对象的三大基本特征和五大基本原则

特征:封装,继承,多态 原则:单一职责原则(SRP) 一个类应该有且只有一个去改变它的理由,这意味着一个类应该只有一项工作。 开放封闭原则(OCP) 对象或实体应该对扩展开放,对修改封闭。例如私有方法允许重载,但不允许重写。 里氏替换原则(LSP) 在对象 x 为类型 T 时 q(x) 成立,那么当 S 是 T 的子类时,对象 y 为类型 S 时 q(y) 也应成立。(即对父类的调用同样适用于子类) 依赖倒置原则(DIP) 高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。具体实现应该依赖于抽象,而不是依赖于实现。依赖于实现会使得日后扩展不断。例如A依赖于B、C、D的实现不如依赖于BCD共同实现的接口或抽象类。 接口隔离原则(ISP) 不应强迫客户端实现一个它用不上的接口,或是说客户端不应该被迫依赖它们不使用的方法,使用多个专门的接口比使用单个接口要好的多!

平台无关性 Java 如何实现的平台无关

java的运行依赖于java运行环境(JRE),Java虽然平台无关但JRE是平台相关的,虽然它支持大部分主流平台。

JVM 还支持哪些语言

(Kotlin、Groovy、JRuby、Jython、Scala)

值传递 值传递、引用传递

1.基本类型作为参数传递时,是传递值的拷贝,无论你怎么改变这个拷贝,原值是不会改变的 2.对象作为参数传递时,是把对象在内存中的地址拷贝了一份传给了参数。 3.C++中还有指针传递。

为什么说Java中只有值传递?

前述的引用传递,形参拿到的是实参地址的复制,相当于是地址值的传递。 Java中的引用传递相当于c++中的指针传递。至于C++中的引用传递解释如下:

引用参数传递过程中,被调函数的形式参数也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参(本体)的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量(根据别名找到主调函数中的本体)。因此,被调函数对形参的任何操作都会影响主调函数中的实参变量。

2. java基础知识 浮点数精度

浮点数在机器中的表示由三部分组成:符号,指数,尾数。所以其最大精度由尾数位数来决定。

包装类型

包装类型用于经基本类型转换为一个对象,包装类于基本类型之间的自动转换称为自动拆装箱。

Integer的缓存机制

在 Java 5 中,为 Integer 的操作引入了一个新的特性,用来节省内存和提高性能。整型对象在内部实现中通过使用相同的对象引用实现了缓存和重用。上面的规则适用于整数区间 -128 到 +127。这种 Integer 缓存策略仅在自动装箱(autoboxing)的时候有用,使用构造器创建的 Integer 对象不能被缓存。Java 编译器把原始类型自动转换为封装类的过程称为自动装箱(autoboxing),这相当于调用 valueOf 方法。这种缓存行为不仅适用于Integer对象。我们针对所有整数类型的类都有类似的缓存机制。 有 ByteCache 用于缓存 Byte 对象 有 ShortCache 用于缓存 Short 对象 有 LongCache 用于缓存 Long 对象 有 CharacterCache 用于缓存 Character 对象 Byte,Short,Long 有固定范围: -128 到 127。对于 Character, 范围是 0 到 127。除了 Integer 可以通过参数改变范围外,其它的都不行。

String JDK 6 和 JDK 7 中 substring 的原理及区别

String是通过字符数组实现的。在jdk 6 中,String类包含三个成员变量:char value[], int offset,int count。他们分别用来存储真正的字符数组,数组的第一个位置索引以及字符串中包含的字符个数。 当调用substring方法的时候,会创建一个新的string对象,但是这个string的值仍然指向堆中的同一个字符数组。这两个对象中只有count和offset 的值是不同的。 这样做乍看起来可以减少内存占用,可是substring的存在会使得整个字符串在无用后得不到回收。所以在JDK7中substring实现方式变为开辟新的内存存储子字符串。

字符串拼接的方式

加号“+” String contact() 方法 StringUtils.join() 方法 StringBuffer append() 方法 StringBuilder append() 方法

stringbuild和stringbuffer的区别

在执行速度方面的比较:StringBuilder > StringBuffer StringBuffer与StringBuilder,他们是字符串变量,是可改变的对象,每当我们用它们对字符串做操作时,实际上是在一个对象上操作的,不像String一样创建一些对象进行操作,所以速度就快了。 StringBuilder:线程非安全的 StringBuffer:线程安全的

String.valueOf 和 Integer.toString 的区别

String.valueOf()对不同数据类型实现了重载,对于int类型的参数直接调用Integer.toString()

字符串池、常量池(运行时常量池、Class 常量池)、intern

String有两种赋值方式,第一种是通过“字面量”赋值。 String str = "Hello"; 第二种是通过new关键字创建新对象。 String str = new String("Hello");

class常量池:我们写的每一个Java类被编译后,就会形成一份class文件;class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool tle),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References); 运行时常量池:运行时常量池存在于内存中,也就是class常量池被加载到内存之后的版本,不同之处是:它的字面量可以动态的添加(String.intern()),符号引用可以被解析为直接引用. 字符串池为他们的一部分。

字面量创建字符串会先在字符串池中找,看是否有相等的对象,没有的话就在堆中创建,把地址驻留在字符串池;有的话则直接用池中的引用,避免重复创建对象。 new关键字创建时,前面的操作和字面量创建一样,只不过最后在运行时会创建一个新对象,变量所引用的都是这个新对象的地址。

由于不同版本的JDK内存会有些变化,JDK1.6字符串常量池在永久代(即方法区),1.7移到了堆中(与运行时常量池分开),1.8用元空间代替了永久代。但是基本对上面的结论没有影响,思想是一样的。

各种关键字 transient、instanceof、final、static、volatile、synchronized、const 原理及用法

transient 表示无需序列化的变量 final可以用来修饰类(继承),方法(重写)和变量(const)(成员变量或局部变量) finally作为异常处理的一部分,它只能用在try/catch语句中 finalize()是在java.lang.Object里定义的,也就是说每一个对象都有这么个方法。这个方法在gc启动,该对象被回收的时候被调用

集合类 ArrayList 和 LinkedList 和 Vector 的区别

ArrayList和Vector都是基于数组实现的,但Vector实现了线程安全所以他效率会低,linkedList是基于双向列表实现的。还有synchronizedList,是实现了线程安全的ArrayList,所以与Vector的区别仅仅是增长速度。

HashMap、HashTable、ConcurrentHashMap 区别

Hashtable和HashMap有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java 5或以上的话,请使用ConcurrentHashMap吧。

HashSet是如何保证元素唯一性的呢?

是通过元素的两个方法,hashCode和equals来完成。 如果元素的HashCode值相同,才会判断equals是否为true。 如果元素的hashcode值不同,不会调用equals。

Java 8 中 stream 相关用法

Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。可以并行化处理。 常用操作有 filter map distinct 排序 reduction collect

apache 集合处理工具类的使用

并交补、过滤、collect(获取某些属性的集合)

不同版本的 JDK 中 HashMap 的实现的区别以及原因

JDK1.8之前处理hash冲突使用链表,但是链表查询满,链表长了之后效率会下降,故JDK1.8之后链表长度达到一定阈值后转换为红黑树,红黑树增删慢,查询快。

Collection与Collections

java.util.Collection 是一个集合接口(集合类的一个顶级接口) Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。

Arrays.asList

是不支持add和remove操作的,也就是说Arrays.asList返回的List是个固定大小的List。如果希望转过后的list可以支持add和remove操作,可使用如下方法:

ArrayList copyArrays=new ArrayList(Arrays.asList(integerArray)); Enumeration 和 Iterator 区别

Iterator除了能读取集合的数据之外,也能数据进行删除操作。 Iterator支持fail-fast机制,而Enumeration不支持。

fail-fast 和 fail-safe

在使用迭代器遍历集合时,若集合内容发生改变,而我们接着对该集合遍历,此事是否应该抛出异常?快速失败就是即刻抛出Concurrent Modification Exception。而安全失败的遍历是在遍历之前将集合内容复制出来,在其上进行遍历。

CopyOnWrite容器

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器

ConcurrentSkipListMap

TreeMap使用红黑树按照key的顺序(自然顺序、自定义顺序)来使得键值对有序存储,但是只能在单线程下安全使用;多线程下想要使键值对按照key的顺序来存储,则需要使用ConcurrentSkipListMap。 ConcurrentSkipListMap的底层是通过跳表来实现的。

同步、异步、阻塞、非阻塞

同步异步是指进程通信方式,阻塞非阻塞是指同步之下进程能否继续执行其他任务。 linux下的五种I/O模型 1)阻塞I/O(blocking I/O) 2)非阻塞I/O(nonblocking I/O) 3)I/O复用(select 和poll) (I/O multiplexing) I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作 4)信号驱动I/O (signal driven I/O (SIGIO)) 在信号处理函数中调用I/O操作函数处理数据,进程继续运行并不阻塞 5)异步I/O (asynchronous I/O (the POSIX aio_functions)) 实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作 前者与后者的区别在于启用异步I/O意味着通知内核启动某个I/O操作,并让内核在整个操作(包括数据从内核复制到用户缓冲区)完成时通知我们。也就是说,异步I/O是由内核通知我们I/O操作何时完成,即实际的I/O操作也是异步的;而 信号驱动I/O是由内核通知我们何时可以启动一个I/O

select、epoll

select的几大缺点及epoll解决方式: 1.每次循环调用select,都需要把fd集合从用户态拷贝到内核态,返回时从内核态拷贝到用户态,这个开销在fd很多时会很大(epoll的解决方案-在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在循环调用epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。) 2.同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大(epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果,和select实现中的第7步是类似的)) 3.select支持的文件描述符数量太小了,默认是1024(fd_set只包含一个int数组,数组大小为1024)

BIO、NIO、AIO

BIO NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。netty基于此 AIO与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。

反射 反射与工厂模式,IOC

将反射与工厂模式结合,可以通过类名字(查找类文件)便能生成对象。可以动态加入类文件。

class Factory{ public static fruit getInstance(String ClassName){ fruit f=null; try{ f=(fruit)Class.forName(ClassName).newInstance(); }catch (Exception e) { e.printStackTrace(); } return f; } }

我们可以把IOC(控制反转)容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言提供的反射机制,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性

代理

静态代理

public void execute() { System.out.println("前拦截..."); bussinessImpl.execute(); System.out.println("后拦截..."); }

动态代理,无需手动实现每个接口,只需添加对方法的判断

public Object getProxyInstance(){ return Proxy.newProxyInstance( targetObject.getClass().getClassLoader(), //和目标对象的类加载器保持一致 targetObject.getClass().getInterfaces(), //目标对象实现的接口,因为需要根据接口动态生成对象 new InvocationHandler() { //InvocationHandler:事件处理器,即对目标对象方法的执行 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("前拦截..."); Object result = method.invoke(proxy, args); System.out.println("后拦截..."); return result; } });

动态代理类并不是程序员写的,而是根据传入的参数,由Proxy类在运行时生成的。 有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口?。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势

序列化 Java序列化底层原理

https://cloud.tencent.com/developer/article/1125165

protobuf

相对于XML,protocol buffers在序列化结构数据时拥有许多先进的特性: 1、更简单 2、序列化后字节占用空间比XML少3-10倍 3、序列化的时间效率比XML快20-100倍 4、具有更少的歧义性 5、自动生成数据访问类方便应用程序的使用

JMS 什么是JMS

JMS 原本就是一个异步的消息服务,客户端获取消息的时候,不需要主动发送请求,消息会自动发送给可用的客户端

Kafka

kafka是什么

泛型

泛型的目的简单地说就是可以让一些运行时才能发现的错误可以在编译期间就可以被编译器所检测出,运行时出问题的代价与编译期出现问题的代价的差别可想而知。换句话说,泛型是编译器的一种及时发现错误的机制,同时也给用户带来了代码的清晰与简洁的附加好处

泛型与继承 public class Solution extends HashMap { void push(Entry x){ super.put(x.getKey(),x.getValue()); } public static void main(String[] args){ Solution b2 = new Solution(); Entry x = new SimpleEntry(1,2); b2.push(x); } } 类型擦除

Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类型在编译后都会被清除掉 List、List擦除后的类型为 List。 List[]、List[] 擦除后的类型为 List[]。 List

list表示列表中可以存放任意类型的元素,List表示该列表中的元素类型可以是任一相同类型,即他是List的父类,为了保证类型安全,不允许对List或List



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3